package ipc;

import graph.Graph;

import java.io.*;
import java.math.BigDecimal;
import java.util.*;

import logic.add.AADD;

import dd.discrete.ADD;
import dd.discrete.ADDDNode;
import dd.discrete.ADDINode;
import dd.discrete.ADDNode;
import dd.discrete.DD;

import rddl.*;
import rddl.RDDL.*;
import rddl.competition.Client;
import rddl.parser.*;
import rddl.policy.Policy;
import rddl.viz.RDDL2Graph;
import util.*;
import rddl.State;

public class ValueIterationAADD {

	public final static int     STATE_ITER  = 0;
	public final static int     OBSERV_ITER = 1;
	
	public static double EPSILON = 0.1;
	
	public static final int ORDER_XXP = 1;
	public static final int ORDER_XPX = 3;
	public static final int ORDER_GUESS = 7;
	public static final int ORDER_INTERLEAVE = 8;
	private static final int BASE_ORDERID = 216;
	
	public State      _state;
	public INSTANCE   _i;
	public NONFLUENTS _n;
	public DOMAIN     _d;
	
	public AADD         _context;
	public ArrayList<Integer> _alSaveNodes;
	public int         DD_ZERO;
	public int         DD_ONE;

	public TreeMap<String, Pair<PVAR_NAME, ArrayList<LCONST> > > _hmStateVars;
	public TreeMap<String, Pair<PVAR_NAME, ArrayList<LCONST> > > _hmNextStateVars;
	public TreeMap<String,ArrayList<PVAR_INST_DEF>> _hmActionMap;
	public TreeMap<String, Pair<PVAR_NAME, ArrayList<LCONST> > > _hmObservVars;
	public HashMap<Integer, Integer> _hmPrimeRemap;
	private TreeMap<PVAR_NAME, ArrayList<ArrayList<LCONST>>> action_vars;
	
	public TreeMap<Pair, Integer> _var2transDD;
	public TreeMap<Pair, Integer> _var2observDD;
	public TreeMap<String, Integer> _act2rewardDD;
	
	private int _valueDD;
	private int _rewardDD;
	
	private int _order;
	private ArrayList<String> _alOrder;
	
//	private Map<String, Integer> _hmAct2ID = new HashMap<String, Integer>();
//	
//	private Map<Integer, String> _hmID2Act = new HashMap<Integer, String>();
	
	private Policy policy;
	
	public HashMap<String , Integer > _hmVar2ID = new HashMap<String, Integer>();
	public HashMap<Integer, String > _hmID2Var = new HashMap<Integer, String>();
	private Map<String, Integer> _hmAct2ID = new HashMap<String, Integer>();
	private Map<Integer, String> _hmID2Act = new HashMap<Integer, String>();
	
	public List<iterationData> iData = new ArrayList<iterationData>(); // :-)
	public preSolve presolve;
	
	public double avgR;
	public double varR;
	
	public long timetoplay;
	public postSolve postsolve;
	
	public Map<Integer, String> _hmactvaluedd2act = new HashMap<Integer, String>();
	
	public runInfo rInfo;
	
	private TreeSet<Pair<Integer, Integer>> _hsPairConstraints = new TreeSet<Pair<Integer,Integer>>();
	private ArrayList<String> _alactionvars = new ArrayList<String>();
	private ArrayList<String> _alstatevars = new ArrayList<String>();
	private ArrayList<String> _alnextstatevars = new ArrayList<String>();
	private ArrayList<String> _alObservvars = new ArrayList<String>();
	public static final double BIGADD = 1e6;
	private static final boolean	DEBUG0	= true;
	private static final boolean	DEBUG2	= true;
	private static final boolean	DEBUG4	= false;
	private static final Runtime	RUNTIME	=  Runtime.getRuntime() ;
	
	public ValueIterationAADD(String domain_file, String instance_file, 
			final Double epsilon, final Integer order, 
			final Integer degree, final String dest_dir) throws Exception {
		
		_order= order;
		EPSILON = epsilon;
		
		RDDL _rddltemp = new RDDL();
		
		File domf = new File(domain_file);
		File instf = new File(instance_file);
		
		_rddltemp.addOtherRDDL(parser.parse(domf));
		_rddltemp.addOtherRDDL(parser.parse(instf));
		
		doVI(_rddltemp, degree, dest_dir);
	}
	
	public ValueIterationAADD(RDDL rddl, final double epsilon, final int order, final int degree,
			 final String dest_dir) throws Exception{
		_order = order;
		EPSILON = epsilon;
		doVI(rddl, degree, dest_dir);
	}
	
	private void doVI(RDDL _rddltemp, final int degree, final String dest_dir) throws Exception{
		
		
		this._d= _rddltemp._tmDomainNodes.entrySet().iterator().next().getValue();
		this._i= _rddltemp._tmInstanceNodes.entrySet().iterator().next().getValue();
		this._n= _rddltemp._tmNonFluentNodes.entrySet().iterator().next().getValue();
		
		if( degree != -1){
			_i._nNonDefActions = degree;
		}
		
		rInfo = new runInfo("AADD", _i._sName, _i._nNonDefActions, _d._sDomainName);
		
		_state = new State();
		
		_state.init(_n != null ? _n._hmObjects : null, _i._hmObjects,  
				_d._hmTypes, _d._hmPVariables, _d._hmCPF,
				_i._alInitState, _n == null ? null : _n._alNonFluents, 
				_d._alStateConstraints, _d._exprReward, _i._nNonDefActions);
		
		long _time = System.currentTimeMillis();
		// Build the CPTs
		buildCPTs();
		
		presolve = new preSolve( getSizeofR(), getSizeofP(), getSizeofA(), (System.currentTimeMillis()-_time)/(1000d*60));
		
		_valueDD = DD_ZERO;
		
		int iter = 0;
		boolean done = false;
		
		ArrayList<String> _alSumOrder = new ArrayList<String>();
		makeOrders(_alSumOrder);
		
		_time = System.currentTimeMillis();
		while ( !done ) {
			long itertime = System.currentTimeMillis();
			int Vprev = _valueDD; 
				
			int Vprimed = _context.remapGIDsInt(Vprev, _hmPrimeRemap);
			System.out.println("Aligning");
			
			Vprimed = _context.align(Vprimed);

			if( !_context.verifyOrder(Vprimed) ) {
				System.err.println("V' violates order");
				System.exit(1);
			}
			
			if( DEBUG4 ) {
				System.out.println("Displaying V'");
				_context.getGraph(Vprimed).launchViewer();
				System.out.println("Press key");
				System.in.read();
			}
			
			//SANITY Check : Vprimed does not contain unprimed variables
			if( DEBUG0 ) {
				Set nodes = _context.getGIDs(Vprimed);
				for( String s : _alstatevars ) {
					if( nodes.contains(_hmVar2ID.get(s) ) ) {
						System.err.println("V' contains x");
						System.exit(1);
					}
				}
				for( String a : _alactionvars ) {
					if( nodes.contains(_hmVar2ID.get(a) ) ) {
						System.err.println("V' contains a");
						System.exit(1);
					}
				}
			}
			
			Set gids = _context.getGIDs(Vprimed);
			
			for( String act : _hmActionMap.keySet() ){
				
				int temp = Vprimed;
				
				if( DEBUG2 ) {
					System.out.println("Regressing via " + act);
				}
				
				if( DEBUG2 ) {
					System.out.println("size of V' before zero constraint : " + _context.countExactNodes(Vprimed));	
				}
				
				temp = applyZeroOneConstraints(temp, act, action_vars, Vprev, Vprimed);
				

				if( DEBUG2 ) {
					System.out.println("size of V' after constraint : " + _context.countExactNodes(Vprimed));	
				}
				
				if( DEBUG4 ) {
					System.out.println("size of V' after constraint : " + _context.countExactNodes(Vprimed));
					System.out.println("Displaying V' after zero constraints");
					_context.getGraph(temp).launchViewer();
					System.out.println("Press key");
					System.in.read();
				}
				
				for( String x : _alSumOrder ){
					String xprimed = x;
					int xprimedid = (Integer)(_hmVar2ID.get(xprimed));
					
					if( ! gids.contains(xprimedid) ){
						if( DEBUG2 ) {
							System.out.println("Skipped " + xprimed);	
						}
						continue;
					}
//					
					int dualdd = _var2transDD.get(new Pair(act, x));
					
					if( DEBUG2 ) {
						System.out.println("Multiplying " + xprimed);
					}
					
					temp = _context.applyInt(temp, dualdd, DD.ARITH_PROD );
					
					if( DEBUG2 ) {
						System.out.println("Summing " + xprimed);
					}
					
					temp = _context.opOut(temp, xprimedid , DD.ARITH_SUM );
					
					if( DEBUG2 ) {
						System.out.println("Expectation " + xprimed);
						System.out.println(" size: " + _context.countExactNodes(temp));
						System.out.println(showMemory());
					}
					
					flushCaches(temp, Vprev, Vprimed);
				}
					
				if( DEBUG0 ) {
					Set nodes = _context.getGIDs(temp);
					for( String ns : _alnextstatevars ) {
						if( nodes.contains(_hmVar2ID.get(ns) ) ) {
							System.err.println("EV' contains x'");
							System.err.println("Displaying V'");
							_context.getGraph(temp).launchViewer();
							System.out.println("Press key");
							System.in.read();
							System.exit(1);
						}
					}
				}
				
				temp = _context.scalarMultiply(temp, _i._dDiscount);

				if( DEBUG2 ) {
					System.out.println("Size before making negative infs. to EV'");
					System.out.println(_context.countExactNodes(temp));
				}
				
				temp = applyConstraintsInterleaved(temp, act, action_vars, Vprev, Vprimed);
				
				if( DEBUG2 ) {
					System.out.println("Size after making negative infs. to EV'");
					System.out.println(_context.countExactNodes(temp));
				}
				
				if( DEBUG2 ) {
					System.out.println("Adding reward");
				}
				
				temp = _context.applyInt(temp, _act2rewardDD.get(act), DD.ARITH_SUM );
						
				temp = _context.applyInt(temp, _rewardDD, DD.ARITH_SUM );
				
				if( DEBUG2 ) {
					System.out.println("Size of V before constraints " + _context.countExactNodes(temp));
					System.out.println(showMemory());
				}
				
				temp = applyConstraintsInterleaved(temp, act, action_vars, Vprev, Vprimed);
				
				if( DEBUG2 ) {
					System.out.println("MAXing");
				}
				
				int Vnext = _context.applyInt(_valueDD, temp, DD.ARITH_MAX);
				
				_valueDD = Vnext;
				
				_hmactvaluedd2act.put(temp, act);
				
				flushCaches(Vprev, Vprimed);
				
				
			}
			
			int diff  = _context.applyInt(_valueDD, Vprev, DD.ARITH_MINUS);
			
//			_context.getGraph(Vprev).launchViewer();
			
//			_context.setMinMax(diff);
			double max_pos_diff = _context.getMax(diff);
            double max_neg_diff = _context.getMin(diff);
            
//            System.out.println(max_pos_diff + " " + max_neg_diff );
            
            double error = Math.max(Math.abs(max_pos_diff), Math.abs(max_neg_diff));

			++iter;
			System.out.println("iteration " + iter + " BE = " + error);// + " nodes " + _context.countExactNodes(_valueDD) + " "  + (System.currentTimeMillis()-itertime));
			iData.add( new iterationData(iter, getSizeofV(), 0, (System.currentTimeMillis()-itertime)/(1000d*60), error, 0 ) );

//			_context.enu
//			_context.enumeratePathsAdd(_valueDD, _hmID2Var);
//			int add = _context.getADD(_valueDD, _hmID2Var);
//			_context.showADD(_valueDD, _hmID2Var);
//			ADDDifference.hereIsMyAdd(_context.getADDContext(), add);
			
//			System.in.read();
			
			if( iter == _i._nHorizon || error <= EPSILON ){
				done = true;
			}else{
				_hmactvaluedd2act.clear();
			}
			
			flushCaches();
		}
		
		long timetosolve = System.currentTimeMillis()-_time;
		
//		Graph g = _context.getGraph(_valueDD, _hmID2Var);
		
//		g.genDotFile(dest_dir+_i._sName+"_"+_i._nNonDefActions+".dot");
//		g.launchViewer();
//		
//		Thread.sleep(10000000);
		
		playDD();
		
		postsolve = new postSolve(avgR, varR, getSizeofV(), 0, timetoplay/(1000d*60), timetosolve/(1000d*60));
	}
	
	public String showMemory() {
		return ((RUNTIME.totalMemory()-RUNTIME.freeMemory())/1e6d) + " / " + (RUNTIME.totalMemory()/1e6d);
	}
	
	private double extent(int dd) {
//		HashSet leaves = new HashSet();
//		_context.collectLeaves(dd, leaves);
//		System.out.println(leaves);
		
		logic.add.ADDNode root = _context.getNode(dd);
		
		if( root instanceof logic.add.ADDDNode){
			return 0;
		}else{
			logic.add.ADDINode node = (logic.add.ADDINode)root; 
			System.out.println(node._dMaxLower+" " + node._dMinLower);
			return node._dMaxLower-node._dMinLower;
		}
	}
	
	public void makeOrders(ArrayList<String> sumOrder){	
		sumOrder.clear();

		Set<String> nextstatevars = _hmNextStateVars.keySet();
		
		for( String var : _alOrder ){
			if( nextstatevars.contains(var) ){
				sumOrder.add(var);
			}
		}
			
//		Collections.reverse(sumOrder);
		
		if( DEBUG0 ){
			System.out.println("Expectation order: " + sumOrder);
		}
	}
	
	private long getSizeofV() {
		return _context.countExactNodes(_valueDD);
	}

	private long getSizeofP() {
		long ret = 0;
		
		
		for( Integer i : _var2transDD.values() ){
			ret += _context.countExactNodes(i);//dual dd count
		}
		return ret;
	}

	private long getSizeofR() {
		long ret = 0;
		
		ret += _context.countExactNodes(_rewardDD); //rewarddd is zero
		
		for( Integer i : _act2rewardDD.values() ){
			ret += _context.countExactNodes(i);
		}
		
		return ret;///_act2rewardDD.size();
	}

	private long getSizeofA() {
		return _hmAct2ID.size();
	}


	public void playDD(){
		
		policy  = new Policy() {
			
			public double avg_reward = 0 ;
			public double variance = 0;//sum of squares till done
			public int horizon;
			public int numRounds = 30;
			private long ptime;
			
			@Override
			public void roundEnd(double reward) {
//				super.roundEnd(reward);
				avg_reward += reward;
				variance += (reward*reward);
			}
			
			@Override
			public void sessionEnd(double total_reward) {
//				super.sessionEnd(total_reward);
				avg_reward /= numRounds;
				variance -= numRounds*(avg_reward*avg_reward);
				variance /= (numRounds-1);
				variance = Math.sqrt(variance);
//				System.out.println("Average reqward : " + avg_reward);
//				System.out.println("Stdev: " + variance);
				avgR = avg_reward;
				varR = variance;
				timetoplay = System.currentTimeMillis()-ptime;
			}
			
			@Override
			public void roundInit(double time_left, int horizon, int round_number, int total_rounds) {
//				super.roundInit(time_left, horizon, round_number, total_rounds);
				this.horizon = horizon;
				this.ptime = System.currentTimeMillis();
			}
			
			
			
			@Override
			public ArrayList<PVAR_INST_DEF> getActions(State s) throws EvalException {
				
				System.out.println(s);
				
				ArrayList<PVAR_INST_DEF> ret = new ArrayList<PVAR_INST_DEF>();
				
				Boolean[] assign = new Boolean[_hmID2Var.size()];
				
				for( Map.Entry<String, Pair<PVAR_NAME, ArrayList<LCONST> > > entry : _hmStateVars.entrySet() ){
					assign[_hmVar2ID.get(entry.getKey())-BASE_ORDERID] = (Boolean)s.getPVariableAssign(entry.getValue()._o1, entry.getValue()._o2);
				}
				
				ArrayList ass = new ArrayList(assign.length);
				
//				System.out.println(ass);
				
				for( Boolean a : assign ){
					ass.add(a);
				}
				
				System.out.println(ass);
				
				double maxval = Double.NEGATIVE_INFINITY;
				String maxa = null;
				
				for( Map.Entry<Integer, String> entry :  _hmactvaluedd2act.entrySet() ){
					double thisval = _context.evaluate(entry.getKey(), ass);
					
					System.out.println(entry.getValue() + " " + thisval );
					
					if( thisval > maxval ){
						maxval = thisval;
						maxa = entry.getValue();
						
					}
				}
				
				return _hmActionMap.get(maxa);
				
			}
		
			public double getAvg_reward() {
				return avg_reward;
			}
			
			public double getVariance() {
				return variance;
			}
			
		};
		
		
		Client cl = new Client(this._d, this._i, this._n, policy, "localhost", 2316, "BEAVER!!!");
		
	}
	
	private int applyConstraintsInterleaved(int dd, String action, 
			TreeMap<PVAR_NAME, ArrayList<ArrayList<LCONST>>> action_vars, int... save){
		int ret = dd;
		Iterator<Pair<Integer, Integer>> it = _hsPairConstraints.iterator();
		
		int[] saved = new int[save.length+1];
		for( int i = 0 ; i < save.length; ++i ){
			saved[i] = save[i];
		}
		
		while( it.hasNext() ){
			Pair<Integer, Integer> con = it.next();
			int proder = simplify(con._o1, _hmActionMap.get(action), action_vars);
			ret = _context.applyInt(ret, proder,	DD.ARITH_PROD);
			
			int adder = simplify(con._o2, _hmActionMap.get(action), action_vars);
			ret = _context.applyInt(ret, adder, DD.ARITH_SUM);
			
			saved[save.length] = ret;
			flushCaches(saved);
		}
		
		return ret;
		
	}
	
	public void flushCaches(int... save){
		_context.clearSpecialNodes();
		
		_context.addSpecialNode(_rewardDD);
		_context.addSpecialNode(_valueDD);
		
		for( int special : _alSaveNodes ){
			_context.addSpecialNode(special);
		}
		
		for( int special : save ){
			_context.addSpecialNode(special);
		}
		
		for( Pair<Integer,Integer> special : _hsPairConstraints ){
			_context.addSpecialNode(special._o1);
			_context.addSpecialNode(special._o2);
		}
		
		for( int val : _hmactvaluedd2act.keySet() ){
			_context.addSpecialNode(val);
		}
			
		_context.flushCaches(false);
	}
	
	
	public void buildCPTs() throws Exception {

		_var2transDD = new TreeMap<Pair,Integer>();
		_var2observDD = new TreeMap<Pair,Integer>();
		_act2rewardDD = new TreeMap<String,Integer>();
		
		// Verify no intermediate variables
		if (_state._tmIntermNames.size() > 0)
			throw new Exception("Cannot convert to SPUDD format: contains intermediate variables");

		// Get all variables
		TreeMap<PVAR_NAME,ArrayList<ArrayList<LCONST>>> state_vars  = collectStateVars();
		action_vars = collectActionVars();
		TreeMap<PVAR_NAME,ArrayList<ArrayList<LCONST>>> observ_vars = collectObservationVars();
		
		_hmStateVars = new TreeMap<String, Pair<PVAR_NAME,ArrayList<LCONST>>>();
		_hmNextStateVars = new TreeMap<String, Pair<PVAR_NAME,ArrayList<LCONST>>>();
		
		for (Map.Entry<PVAR_NAME,ArrayList<ArrayList<LCONST>>> e : state_vars.entrySet()) {
			PVAR_NAME p = e.getKey();
			ArrayList<ArrayList<LCONST>> assignments = e.getValue();
			for (ArrayList<LCONST> assign : assignments) {
				String name = CleanFluentName(p.toString() + assign);
				_hmStateVars.put(name, new Pair(p, assign));
				_alstatevars.add(name);
				_hmNextStateVars.put(name + "'", new Pair(p, assign));
				_alnextstatevars.add(name+"'");
			}
		}
		
		//changed - 8/11/11 - does not check for constraints with respect to current state anymore - Aswin
		//constraints are imposed as very high action costs
		_hmActionMap = ActionGenerator.getLegalBoolActionMap(_state);
		for (Map.Entry<PVAR_NAME,ArrayList<ArrayList<LCONST>>> e : action_vars.entrySet()) {
			PVAR_NAME p = e.getKey();
			ArrayList<ArrayList<LCONST>> assignments = e.getValue();
			for (ArrayList<LCONST> assign : assignments) {
				String name = CleanFluentName(p.toString() + assign);
				_alactionvars.add(name);
			}
		}
		
		_hmObservVars = new TreeMap<String, Pair<PVAR_NAME,ArrayList<LCONST>>>();
		
		for (Map.Entry<PVAR_NAME,ArrayList<ArrayList<LCONST>>> e : observ_vars.entrySet()) {
			PVAR_NAME p = e.getKey();
			ArrayList<ArrayList<LCONST>> assignments = e.getValue();
			for (ArrayList<LCONST> assign : assignments) {
				String name = CleanFluentName(p.toString() + assign);
				_hmObservVars.put(name, new Pair(p, assign)); // SPUDD uses primed observation vars
				_alObservvars.add(name);
			}
		}
		
		_alOrder = new ArrayList<String>();
		

		if( _order != ORDER_GUESS ){
			_alOrder = makeOrdering();
		}else{
			_alOrder = guessOrdering(state_vars, observ_vars, action_vars);
		}
		
		int orderid = BASE_ORDERID;
		ArrayList<Integer> _alIntOrder = new ArrayList<Integer>();
		
		for( String var : _alOrder ){
//			System.out.println(var);
			Integer id = _hmVar2ID.get(var);
			if( id == null ){
				_hmVar2ID.put(var, orderid );
				_hmID2Var.put(orderid, var);
				_alIntOrder.add(orderid);
				++orderid;
			}else{
				_alIntOrder.add(id);
			}
		}
		
		_hmPrimeRemap = new HashMap<Integer	, Integer>();
		for( String statevar : _hmStateVars.keySet() ){
			_hmPrimeRemap.put( _hmVar2ID.get(statevar), _hmVar2ID.get(statevar+"'") );
		}

		_context = new AADD(_alIntOrder);
		
		DD_ONE = _context.getConstantNode(1d);
		DD_ZERO = _context.getConstantNode(0d);
		_valueDD = DD_ZERO;
		_rewardDD = DD_ZERO;
		
		_alSaveNodes = new ArrayList<Integer>();
		_alSaveNodes.add(DD_ONE);
		_alSaveNodes.add(DD_ZERO);
		
		getConcurrenyDD(action_vars);
//		_context.getGraph(conc_dd).launchViewer();
		
		for( EXPR constraint : _state._alConstraints ){
//			System.out.println("Constraint : " + constraint ); 
			if( constraint instanceof BOOL_EXPR ){
				BOOL_EXPR be = (BOOL_EXPR)constraint;
				
				addConstraint(be);
//				_context.getGraph(dd).launchViewer();
				
			}else{
				System.err.println("Constraint not tpye of bool expr");
				System.exit(1);
			}
		}
		
		///////
		double rmin = 0;
		for (int iter = STATE_ITER; iter <= OBSERV_ITER; iter++) {
			
			// This loop handles both transition and observation construction
			TreeMap<PVAR_NAME,ArrayList<ArrayList<LCONST>>> src = 
				iter == STATE_ITER ? state_vars : observ_vars;
			
			for (Map.Entry<PVAR_NAME,ArrayList<ArrayList<LCONST>>> e : src.entrySet()) {
				
				// Go through all variable names p for a variable type
				PVAR_NAME p = e.getKey();
				ArrayList<ArrayList<LCONST>> assignments = e.getValue();
				//System.out.println(_state._hmCPFs);
				
				CPF_DEF cpf = _state._hmCPFs.get(new PVAR_NAME(p.toString() + 
						(iter == STATE_ITER ? "'" : "")));
				
				HashMap<LVAR,LCONST> subs = new HashMap<LVAR,LCONST>();
				for (ArrayList<LCONST> assign : assignments) {
	
					String cpt_var = CleanFluentName(p.toString() + assign);
//					System.out.println("Processing: " + cpt_var);
	
					subs.clear();
					for (int i = 0; i < cpf._exprVarName._alTerms.size(); i++) {
						LVAR v = (LVAR)cpf._exprVarName._alTerms.get(i);
						LCONST c = (LCONST)assign.get(i);
						subs.put(v,c);
					}
					
					// This method determines all ground fluents relevant to 
					// the cpf for a variable for the given substitution 'subs'
					HashSet<Pair> relevant_vars = new HashSet<Pair>();
					EXPR cpf_expr = cpf._exprEquals;
					if (_d._bCPFDeterministic) // collectGFluents expects a distribution so convert to a Delta function if needed
						cpf_expr = new KronDelta(cpf_expr);					
					
					cpf_expr.collectGFluents(subs, _state, relevant_vars);
		
					// Filter out action vars if we are not doing a concurrent encoding
					// with actions as state variables
					relevant_vars = filterOutActionVars(relevant_vars);
					
					// Go through all actions and initialize state assignment to false 
					for (Map.Entry<PVAR_NAME,ArrayList<ArrayList<LCONST>>> e2 : action_vars.entrySet()) {
	
						PVAR_NAME action_name = e2.getKey();
						ArrayList<ArrayList<LCONST>> action_assignments = e2.getValue();
						
						Boolean defaultval = (Boolean)_state.getDefaultValue(action_name);
	
						// Go through instances for action_name
						for (ArrayList<LCONST> action_assign : action_assignments)
							_state.setPVariableAssign(action_name, action_assign, defaultval);
	
					}
					
					// Now go through all actions, setting each one to be true in turn

						int actid = 0;
						
						
					for (Map.Entry<String,ArrayList<PVAR_INST_DEF>> e2 : _hmActionMap.entrySet()) {
						// Clear out ADD caches if required
						flushCaches();

						String action_instance = e2.getKey();
						
						ArrayList<PVAR_INST_DEF> action_list = e2.getValue();

						_hmAct2ID.put(action_instance, actid);
						_hmID2Act.put(actid, action_instance);
						actid++;
						
						// Set all pvariables in action
						for (PVAR_INST_DEF pid : action_list){
							_state.setPVariableAssign(pid._sPredName, pid._alTerms, RDDL.BOOL_CONST_EXPR.TRUE);
						}

						//////////////////////
						
						// Build action-specific reward
						// taking into account constraint - 8/11/11 aswin
						Integer rew_fun = _act2rewardDD.get(action_instance);
						if (rew_fun == null) {
							//HashSet<Pair> rew_relevant_vars = new HashSet<Pair>();
							
							//HashMap<LVAR,LCONST> empty_sub = new HashMap<LVAR,LCONST>();
							EXPR rew_expr =  _state._reward;
							ArrayList<Integer> rewdd = convertAddExpr2ADD(rew_expr, true);
							
							int rdd = DD_ZERO; 
							
							for( int dd : rewdd ){
								rdd = _context.applyInt(rdd, dd, DD.ARITH_SUM);
							}
							
							double thisrmin = Math.abs(_context.getMin(rdd));
							rmin = thisrmin > rmin ? thisrmin : rmin;
							
//							_context.getGraph(rdd).launchViewer();
//							System.in.read();
							//apply constraint will also simplify the constraint for this action
//							rdd = applyConstraints(rdd, action_list, action_vars);
							
							_act2rewardDD.put(action_instance, rdd);
							_alSaveNodes.add(rdd);
						}							
							
						int cpt = enumerateAssignments(new ArrayList<Pair>(relevant_vars), cpf_expr, subs, 0);
						
						//build dual dd
						int cptvarid = _hmVar2ID.get(cpt_var+"'");
						
						int true_br = _context.getVarNode( cptvarid, 0.0d, 1.0d);
						true_br = _context.applyInt(true_br, cpt, DD.ARITH_PROD);
						
						int false_br = _context.getVarNode( cptvarid, 1.0d, 0.0d);
						int oneminuscpt = _context.applyInt(DD_ONE, cpt, DD.ARITH_MINUS);
						false_br = _context.applyInt(false_br, oneminuscpt , DD.ARITH_PROD);
						
						cpt = _context.applyInt(true_br, false_br, DD.ARITH_SUM);
						
						_alSaveNodes.add(cpt);
						
						if (iter == STATE_ITER)
							_var2transDD.put(new Pair(action_instance, cpt_var+"'"), cpt);
						else
							_var2observDD.put(new Pair(action_instance, cpt_var + "'"), cpt);

						if( DEBUG4 ) {
							System.out.println("Displaying CPT for " + cpt_var+"'");
							_context.getGraph(cpt, _hmID2Var).launchViewer();
							System.out.println("Press any key");
							System.in.read();
						}
						//////////////////////
						
						// Unset all pvariables in action
						for (PVAR_INST_DEF pid : action_list){
							_state.setPVariableAssign(pid._sPredName, pid._alTerms, null);
						}
					}
					
				}
			}
		}
		
		
		
		for( Map.Entry<String, Integer> entry : _act2rewardDD.entrySet() ){
			int oldcostdd = entry.getValue();
			int adder = _context.getConstantNode((1+rmin));
			int newcostdd = _context.applyInt(oldcostdd, adder, DD.ARITH_SUM);
			newcostdd = applyConstraintsInterleaved(newcostdd, entry.getKey(), action_vars );
			entry.setValue(newcostdd);
			_alSaveNodes.remove((Integer)oldcostdd);
			_alSaveNodes.add(newcostdd);
		}
		
		_rewardDD = DD_ZERO;
		_alSaveNodes.add(_rewardDD);
	}
	
	private int applyZeroOneConstraints(int dd, String act, TreeMap<PVAR_NAME, ArrayList<ArrayList<LCONST>>> action_vars
			, int... save) {
		
		int ret = dd ; 
		
		int[] saved = new int[save.length+1];
		for( int i  = 0 ; i < save.length; ++i ) {
			saved[i] = save[i];
		}
		
		for(Pair<Integer,Integer> con : _hsPairConstraints ){
			int adder = simplify(con._o1, _hmActionMap.get(act), action_vars);
			ret = _context.applyInt(ret, adder , DD.ARITH_PROD);
			saved[save.length] = ret;
			flushCaches(saved);
		}
		
		return ret;
	}

	private void addConstraint(BOOL_EXPR be) throws Exception {
		HashMap<LVAR, LCONST> empty = new HashMap<LVAR, LCONST>();
		HashSet<Pair> gfluents = new HashSet<Pair>();
		be.collectGFluents(empty, _state, gfluents);
		
		IF_EXPR ife = new IF_EXPR(be, new KronDelta(new BOOL_CONST_EXPR(true)), 
				new KronDelta(new BOOL_CONST_EXPR(false)) );
		
		int zerodd = enumerateAssignments(new ArrayList<Pair>(gfluents), ife, empty	, 0 );
		int neginfdd = _context.applyInt(DD_ONE, zerodd, DD.ARITH_MINUS);
		neginfdd = _context.applyInt(neginfdd, _context.getConstantNode(-1234d), DD.ARITH_PROD);

		_hsPairConstraints.add(new Pair<Integer,Integer>(zerodd,neginfdd));
		
		if( DEBUG4 ) {
			System.out.println("Displaying constraint : " + be);
			_context.getGraph(zerodd).launchViewer();
			_context.getGraph(neginfdd).launchViewer();
			System.out.println("Press key...");
			System.in.read();
		}
		
		
	}

	//added 8/14/11
	public ArrayList<Integer> convertAddExpr2ADD(EXPR e, boolean filter_actions) throws Exception {
		
		ArrayList<Integer> adds = new ArrayList<Integer>();
		ArrayList<Pair> exprs = getAdditiveComponents(e);
		
//		System.out.println("\n");
		for (Pair p : exprs) {
			String str = "";
			if (p._o2 instanceof RDDL.OPER_EXPR)
				str = ((RDDL.OPER_EXPR)p._o2)._op;
//			System.out.println("Found pair: " + p._o1 + " -- " + p._o2.getClass() + " / " + str + "\n" + p);
		}
		
		int ZERO_ADD = _context.getConstantNode(0d); 
		
		for (Pair p : exprs) {
			
			HashSet<Pair> relevant_vars = new HashSet<Pair>();
			HashMap subs = (HashMap)p._o1;
			EXPR e2 = ((EXPR)p._o2);
			if (_d._bRewardDeterministic) // collectGFluents expects distribution
				e2 = new DiracDelta(e2);
			else{
//				System.out.println("WARNING: May not convert additive reward correctly... check results.");
			}
			e2.collectGFluents(subs, _state, relevant_vars);
			
			if (filter_actions)
				relevant_vars = filterOutActionVars(relevant_vars);
			
			
			int add = enumerateAssignments(new ArrayList<Pair>(relevant_vars), e2, subs, 0);
			if (add != ZERO_ADD)
				adds.add(add);
		}
//		System.out.println("Done processing additive expression");
		
		return adds;
	}
	
	// TODO: Make this recursive (append subs, ...)
	// TODO: Make an expression to additive expression converter to be called in buildCPTs()
	// Returns Pair(HashMap subs, EXPR e)
	public ArrayList<Pair> getAdditiveComponents(EXPR e) throws Exception {

		ArrayList<Pair> ret = new ArrayList<Pair>();
		
		if (e instanceof OPER_EXPR && ((OPER_EXPR)e)._op == OPER_EXPR.PLUS) {

			OPER_EXPR o = (OPER_EXPR)e;

			//System.out.println("\n- Oper Processing " + o._e1);
			//System.out.println("\n- Oper Processing " + o._e2);
			
			ret.addAll(getAdditiveComponents(o._e1));
			ret.addAll(getAdditiveComponents(o._e2));
						
		} else if (e instanceof AGG_EXPR && ((AGG_EXPR)e)._op == AGG_EXPR.SUM) {
			
			AGG_EXPR a = (AGG_EXPR)e;
			
			ArrayList<ArrayList<LCONST>> possible_subs = _state.generateAtoms(a._alVariables);
			HashMap<LVAR,LCONST> subs = new HashMap<LVAR,LCONST>();

			//System.out.println("\n- Sum Processing " + a);

			// Evaluate all possible substitutions
			for (ArrayList<LCONST> sub_inst : possible_subs) {
				for (int i = 0; i < a._alVariables.size(); i++) {
					subs.put(a._alVariables.get(i)._sVarName, sub_inst.get(i));
				}
				
				// Note: we are not currently decomposing additive structure below a sum aggregator
				ret.add(new Pair(subs.clone(), a._e));			
				
				subs.clear();
			}

		} else {
			//System.out.println("\n- General Processing " + e);
			HashMap<LVAR,LCONST> empty_subs = new HashMap<LVAR,LCONST>();
			ret.add(new Pair(empty_subs, e));
		}
		
		return ret;
	}
	
	
	
//	private int applyNegInfConstraints(int dd, String action, 
//			HashMap<PVAR_NAME, ArrayList<ArrayList<LCONST>>> action_vars) {
//		
//		int ret = dd ; 
//		for(int con : _hsConstraints ){
//			int adder = simplify(con, _hmActionMap.get(action), action_vars);
//			ret = _context.applyInt(ret, adder , DD.ARITH_SUM);
//		}
//		
//		return ret;
//		
//	}

	private int simplify(int con, ArrayList<PVAR_INST_DEF> action_list, 
			TreeMap<PVAR_NAME, ArrayList<ArrayList<LCONST>>> action_vars) {
		//simplify action vars
		int ret = con;
		
		for( Map.Entry<PVAR_NAME, ArrayList<ArrayList<LCONST>>> entry : action_vars.entrySet() ){
			//see if its in action_list
			PVAR_NAME pvar = entry.getKey();
			ArrayList<ArrayList<LCONST>> assigns = entry.getValue();
			Boolean defaultval = (Boolean)_state.getDefaultValue(pvar);
			
			for( ArrayList<LCONST> terms : assigns ){
				PVAR_INST_DEF thisone = new PVAR_INST_DEF(pvar.toString(), true, terms );
				Boolean val = action_list.contains(thisone) ? !defaultval : defaultval;
				String actname = CleanFluentName(pvar.toString()+terms);
				int actid = _hmVar2ID.get(actname);
				
				ret = _context.restrict(ret, actid, val ? DD.RESTRICT_HIGH : DD.RESTRICT_LOW);
			}
			
		}
		
		return ret;
	}

	private void getConcurrenyDD(TreeMap<PVAR_NAME, ArrayList<ArrayList<LCONST>>> action_vars) throws Exception {
		
		EXPR concexpr = null;
		
		int xid = 1;
		for( Map.Entry<PVAR_NAME, ArrayList<ArrayList<LCONST>>> entry : action_vars.entrySet() ){
			
			PVAR_NAME pvar = entry.getKey();
			PVARIABLE_DEF pvardefn = _state._hmPVariables.get(pvar);
			
			ArrayList<LTYPED_VAR> sumover = new ArrayList<LTYPED_VAR>();
			ArrayList<LTERM> subs = new ArrayList<LTERM>();
			
			ArrayList<TYPE_NAME> params = pvardefn._alParamTypes;
			int yid = 1;
			for( TYPE_NAME type : params ){
				String varname = "x"+(xid)+(yid);
				sumover.add(new LTYPED_VAR(varname, type._STypeName ));
				subs.add(new LVAR(varname));
				++yid;
			}
			
//			System.out.println(sumover.size());
			
			PVAR_EXPR inner = new PVAR_EXPR(pvar.toString(), subs);
			EXPR summer = ( sumover.size() == 0 ) ? inner : new AGG_EXPR(AGG_EXPR.SUM, sumover, inner);
			
			concexpr = ( concexpr == null ) ? summer : new OPER_EXPR(summer, concexpr, OPER_EXPR.PLUS);
			++xid;
		}
		
		concexpr = new COMP_EXPR(concexpr, new INT_CONST_EXPR(_i._nNonDefActions), COMP_EXPR.LESSEQ);
		
//		System.out.println("Concurrency expression : " + concexpr);
		
//		System.exit(1);

		addConstraint((BOOL_EXPR)concexpr);
//		_context.getGraph(dd).launchViewer();
//		Thread.sleep(10000000);
		
	}
	
	private ArrayList<String> guessOrdering(TreeMap<PVAR_NAME, ArrayList<ArrayList<LCONST>>> state_vars, 
			TreeMap<PVAR_NAME, ArrayList<ArrayList<LCONST>>> observ_vars, TreeMap<PVAR_NAME, ArrayList<ArrayList<LCONST>>> action_vars) throws EvalException {
			
			ArrayList<String> order = new ArrayList<String>();
			
			for (int iter = STATE_ITER; iter <= OBSERV_ITER; iter++) {
				
				TreeMap<PVAR_NAME,ArrayList<ArrayList<LCONST>>> src = 
					iter == STATE_ITER ? state_vars : observ_vars;
				
				for (Map.Entry<PVAR_NAME,ArrayList<ArrayList<LCONST>>> e : src.entrySet()) {
					
					PVAR_NAME p = e.getKey();
					ArrayList<ArrayList<LCONST>> assignments = e.getValue();
					
					CPF_DEF cpf = _state._hmCPFs.get(new PVAR_NAME(p.toString() + 
							(iter == STATE_ITER ? "'" : "")));
					
					HashMap<LVAR,LCONST> subs = new HashMap<LVAR,LCONST>();
					for (ArrayList<LCONST> assign : assignments) {
		
						String cpt_var = CleanFluentName(p.toString() + assign);
//						System.out.println("Processing: " + cpt_var);
						
		
						subs.clear();
						for (int i = 0; i < cpf._exprVarName._alTerms.size(); i++) {
							LVAR v = (LVAR)cpf._exprVarName._alTerms.get(i);
							LCONST c = (LCONST)assign.get(i);
							subs.put(v,c);
						}
						
						// This method determines all ground fluents relevant to 
						// the cpf for a variable for the given substitution 'subs'
						EXPR cpf_expr = cpf._exprEquals;
						
						HashSet<Pair> relvars = new HashSet<Pair>();
						cpf_expr.collectGFluents(subs, _state, relvars);
						
						for( Pair pa : relvars ){
							PVAR_NAME pvar = (PVAR_NAME)pa._o1;
							ArrayList<LCONST> ass = (ArrayList<LCONST>)pa._o2;
							String name = CleanFluentName(pvar.toString()+ass);
							
							if( !order.contains(name) && _state.getPVariableType(pvar) == _state.STATE ){
								order.add(name);	
							}
							
						}
						
						for( Pair pa : relvars ){
							PVAR_NAME pvar = (PVAR_NAME)pa._o1;
							ArrayList<LCONST> ass = (ArrayList<LCONST>)pa._o2;
							String name = CleanFluentName(pvar.toString()+ass);
							
							if( !order.contains(name) && _state.getPVariableType(pvar) == _state.ACTION ){
								order.add(name);	
							}
							
						}
						
						order.add(cpt_var+"'");
						
					}
				}
			}
			
			if( order.size() != 2*_alstatevars.size()  + _alactionvars.size() + _alObservvars.size() ) {
				System.err.println("size of ordering not equal to size of variables");
				System.out.println(order);
				System.out.println(_alstatevars);
				System.out.println(_alactionvars);
				System.out.println(_alObservvars);
				
				System.exit(1);
			}
			
			if( DEBUG0 ) {
				System.out.println("Problem has " + order.size() + " variables ");
				System.out.println("State vars : " + _alstatevars.size());
				System.out.println("Action vars : " + _alactionvars.size());
				System.out.println("Ordering : " + order);	
			}
			
			
			return order;
		}
	private ArrayList<String> makeOrdering() {
		
//		System.out.println(_hmStateVars.keySet());
//		System.out.println(_hmActionMap.keySet());
//		System.out.println(_hmNextStateVars.keySet());
//		
//		
		ArrayList<String> order = new ArrayList<String>();
//		
//		String[] ord = {"person_in_elevator_going_up__e0", "person_in_elevator_going_down__e0", 
//				"elevator_closed__e0", 
//				"elevator_at_floor__e0_f0", "elevator_dir_up__e0", "elevator_at_floor__e0_f1", "elevator_at_floor__e0_f2",
//				"person_waiting_up__f0", "person_waiting_up__f1", "person_waiting_up__f2", 
//				"person_waiting_down__f2", "person_waiting_down__f0", "person_waiting_down__f1"}; 
//		
//		return order;
		
		switch(_order){
			
			
				
			case ORDER_XPX:
				order.addAll(_alnextstatevars);
				order.addAll(_alstatevars);
				
				break;
				
			case ORDER_XXP:

				order.addAll(_alstatevars);
				order.addAll(_alnextstatevars);
			
			default:
				System.err.println("Unknown ordering...");
			
			case ORDER_INTERLEAVE:	
				
				for( String s : _alstatevars ) {
					order.add(s);
					order.add(s+"'");
				}
				break;
		}
		
		order.addAll(_alactionvars);
		
		if( DEBUG0 ) {
			System.out.println("Problem has " + order.size() + " variables");
			System.out.println("Ordering : " + order);
		}
		
		return order;
	}
		
	private HashSet<Pair> filterOutActionVars(HashSet<Pair> relevant_vars) {
		HashSet<Pair> new_vars = new HashSet<Pair>();
		for (Pair p : relevant_vars)
			if (_state.getPVariableType((PVAR_NAME)p._o1) != State.ACTION)
				new_vars.add(p);
		return new_vars;
	}

	public int enumerateAssignments(ArrayList<Pair> vars, 
			EXPR cpf_expr, HashMap<LVAR,LCONST> subs, int index) 
		throws EvalException {
		return enumerateAssignments(vars, cpf_expr, subs, index, _context.getConstantNode(1d));
	}
	
	public int enumerateAssignments(ArrayList<Pair> vars, 
			EXPR cpf_expr, HashMap<LVAR,LCONST> subs, int index, int cpt) 
		throws EvalException {

		// Need to build an assignment ADD on the way down, use it 
		// at leaf to add to current CPT and return result
		
		// Recurse until index == vars.size
		if (index >= vars.size()) {
			
			// At this point, all state and action variables are set
			// Just need to get the distribution for the appropriate CPT
			RDDL.EXPR e = cpf_expr.getDist(subs, _state);
			double prob_true = -1d;
			//System.out.println("RDDL.EXPR: " + e);
			if (e instanceof KronDelta) {
				EXPR e2 = ((KronDelta)e)._exprIntValue;
				if (e2 instanceof INT_CONST_EXPR)
					// Should either be true (1) or false (0)... same as prob_true
					prob_true = (double)((INT_CONST_EXPR)e2)._nValue;
				else if (e2 instanceof BOOL_CONST_EXPR)
					prob_true = ((BOOL_CONST_EXPR)e2)._bValue ? 1d : 0d;
				else
					throw new EvalException("Unhandled KronDelta argument: " + e2.getClass()); 
			} else if (e instanceof Bernoulli) {
				prob_true = ((REAL_CONST_EXPR)((Bernoulli)e)._exprProb)._dValue;
			} else if (e instanceof DiracDelta) {
				// NOTE: this is not a probability, but rather an actual value
				//       (presumably for the reward).  This method is a little
				//       overloaded... need to consider whether there will be
				//       any problems arising from this overloading.  -Scott
				prob_true = ((REAL_CONST_EXPR)((DiracDelta)e)._exprRealValue)._dValue;
			} else
				throw new EvalException("Unhandled distribution type: " + e.getClass());
			
			// Now build CPT for action variables
			return _context.scalarMultiply(cpt, prob_true);

		} else {
			PVAR_NAME p = (PVAR_NAME)vars.get(index)._o1;
			ArrayList<LCONST> terms = (ArrayList<LCONST>)vars.get(index)._o2;
			String var_name = CleanFluentName(p._sPVarName + terms + (p._bPrimed ? "\'" : ""));
//			System.out.println(var_name+"----");
			int varid = _hmVar2ID.get(var_name);
			//System.out.println(var_name);

			// Set to true
			_state.setPVariableAssign(p, terms, RDDL.BOOL_CONST_EXPR.TRUE);
			int high = _context.getVarNode(varid, 0d, 1d);
			int ret_high = enumerateAssignments(vars, cpf_expr, subs, index + 1, _context.applyInt(cpt, high, ADD.ARITH_PROD));

			// Set to false
			_state.setPVariableAssign(p, terms, RDDL.BOOL_CONST_EXPR.FALSE);
			int low  = _context.getVarNode(varid, 1d, 0d);
			int ret_low = enumerateAssignments(vars, cpf_expr, subs, index + 1, _context.applyInt(cpt, low, ADD.ARITH_PROD));
			
			// Unassign
			_state.setPVariableAssign(p, terms, null);
			
			// Sum both sides of the returned CPT and return it
			return _context.applyInt(ret_high, ret_low, ADD.ARITH_SUM);
		}
	}
		
	public TreeMap<PVAR_NAME,ArrayList<ArrayList<LCONST>>> collectStateVars() 
		throws EvalException {
		
		TreeMap<PVAR_NAME,ArrayList<ArrayList<LCONST>>> state_vars = 
			new TreeMap<PVAR_NAME,ArrayList<ArrayList<LCONST>>>();
	
		for (PVAR_NAME p : _state._alStateNames) {
			ArrayList<ArrayList<LCONST>> gfluents = _state.generateAtoms(p);
			state_vars.put(p, gfluents);
		}
		
		return state_vars;
	}
	
	public TreeMap<PVAR_NAME,ArrayList<ArrayList<LCONST>>> collectActionVars() 
		throws EvalException {
		
		TreeMap<PVAR_NAME,ArrayList<ArrayList<LCONST>>> action_vars = 
			new TreeMap<PVAR_NAME,ArrayList<ArrayList<LCONST>>>();
	
		for (PVAR_NAME p : _state._alActionNames) {
			ArrayList<ArrayList<LCONST>> gfluents = _state.generateAtoms(p);
			action_vars.put(p, gfluents);
		}
		
		return action_vars;
	}
	
	public TreeMap<PVAR_NAME,ArrayList<ArrayList<LCONST>>> collectObservationVars() 
		throws EvalException {

		TreeMap<PVAR_NAME,ArrayList<ArrayList<LCONST>>> observ_vars = 
			new TreeMap<PVAR_NAME,ArrayList<ArrayList<LCONST>>>();
	
		for (PVAR_NAME p : _state._alObservNames) {
			ArrayList<ArrayList<LCONST>> gfluents = _state.generateAtoms(p);
			observ_vars.put(p, gfluents);
		}
		
		return observ_vars;
	}
		
	public static String CleanFluentName(String s) {
		s = s.replace("[", "__");
		s = s.replace("]", "");
		s = s.replace(", ", "_");
		s = s.replace(',','_');
		s = s.replace(' ','_');
		s = s.replace('-','_');
		s = s.replace("()","");
		s = s.replace("(", "__");
		s = s.replace(")", "");
		if (s.endsWith("__"))
			s = s.substring(0, s.length() - 2);
		return s;
	}
	
	public static void main(String[] args) throws Exception {
//		new ValueIteration("/scratch/final_comp/rddl/sysadmin_mdp.rddl", 
//				"/scratch/final_comp/rddl/sysadmin_uniring_5_combined.rddl", 
//				0.1, ValueIteration.ORDER_GUESS, -1, "./");
		
//		new ValueIterationAADD("/scratch/final_comp/rddl/elevators_mdp.rddl",
//				"/scratch/final_comp/rddl/elevators_inst_mdp__1.rddl",
//				0.1, ValueIteration.ORDER_GUESS, -1, "./");
		
//		new ValueIterationAADD("/scratch/final_comp/rddl/crossing_traffic_mdp.rddl",
//				"/scratch/final_comp/rddl/crossing_traffic_inst_mdp__1.rddl",
//				0.1, ValueIterationAADD.ORDER_AXPX, -1, "./");
		
//		new ValueIterationAADD("/scratch/final_comp/rddl/skill_teaching_mdp.rddl",
//				"/scratch/final_comp/rddl/skill_teaching_inst_mdp__1.rddl",
//				0.1, ValueIterationAADD.ORDER_AXPX, -1, "./");
		
//		new ValueIterationAADD("/scratch/final_comp/rddl/game_of_life_mdp.rddl",
//				"/scratch/final_comp/rddl/game_of_life_inst_mdp__1.rddl",
//				0.1, ValueIterationAADD.ORDER_AXPX, -1, "./");
		
		ValueIterationAADD viaadd = new ValueIterationAADD(args[0], args[1], Double.parseDouble(args[2]) , ValueIterationAADD.ORDER_GUESS, -1, "./");
		
		System.out.println(viaadd);
	}

	@Override
	public String toString() {
		return "ValueIterationAADD [presolve=" + this.presolve + ", \navgR="
				+ this.avgR + ", \nvarR=" + this.varR + ", \npostsolve="
				+ this.postsolve + ", \nrInfo=" + this.rInfo + "]";
	}
}
